# coding: utf-8
# author: ZhenbPeng
#   date: 2017/10/17 17:04
import random
import time
import logging
import threadpool
from datetime import datetime
from math import sin
from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy, ProxyType
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from contextlib import contextmanager

from settings import Setting
from proxy.tasks import clean_proxyip
from utils.redis_cache import ProxyPool, WorkerStats

logger = logging.getLogger("worker")


class Worker(object):
    START_URL = 'http://home.ttplus.cn/'

    def __init__(self, name, ip, port):
        self.name = name
        self.ip = ip
        self.port = port
        self.proxy = None
        self.driver = None
        self.user_agent = random.Random().choice(Setting.UA)
        self.init_data()
        self.hreflist = set()
        self.href = None
        self.proxystatus = True
        self.oldwindows = 0
        self.newwindows = 0

    def init_data(self):
        self.proxy = Proxy({
                        'proxyType': ProxyType.MANUAL,
                        'httpProxy': '%s:%s' % (self.ip, self.port)  # 代理ip和端口
                    })
        desired_capabilities = DesiredCapabilities.PHANTOMJS.copy()
        desired_capabilities['phantomjs.page.customHeaders.User-Agent'] = self.user_agent
        self.proxy.add_to_capabilities(desired_capabilities)

        self.driver = webdriver.PhantomJS(desired_capabilities=desired_capabilities)
        self.driver.set_page_load_timeout(60)
        self.driver.set_script_timeout(60)

    def list(self):
        return sorted(filter(lambda x: x.startswith("action"), dir(self)), key=lambda x: x.split("_")[-1])

    def do(self, action, *args, **kwargs):
        if self.proxystatus:
            call = getattr(self, action)
            try:
                call(args, kwargs)
            except Exception as e:
                logger.error("Worker[%s] action[%s] error \n %s" % (self.name, action, e))

    def action_98(self, *args, **kwargs):
        try:
            self.driver.get("http://resource.ttplus.cn/h5/ttplus-m/share0.html")
            logger.info("Worker[%s] get url %s" % (self.name, "http://resource.ttplus.cn/h5/ttplus-m/share0.html"))
            time.sleep(1)
            self.driver.execute_script("document.documentElement.scrollTop=10000")
            WorkerStats().add("demo1_load", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))
        except Exception as e:
            self.proxystatus = False
            return

    def action_1(self, *args, **kwargs):
        try:
            self.driver.get(self.START_URL)
            logger.info("Worker[%s] get url %s" % (self.name, self.START_URL))
            WorkerStats().add("action1", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))
        except Exception as e:
            self.proxystatus = False
            return

    def action_2(self, *args, **kwargs):
        try:
            self.hreflist = set(self.driver.find_elements_by_xpath('//li[@class="newsList-item"]/a'))
            logger.info("Worker[%s] get hreflist length=%s" % (self.name, len(self.hreflist)))
            WorkerStats().add("action2", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))
        except Exception as e:
            return

    def action_3(self, *args, **kwargs):
        self.oldwindows = len(self.driver.window_handles)
        while self.hreflist:
            self.href = self.hreflist.pop()
            try:
                self.href.click()
                self.newwindows = self.oldwindows + 1
            except Exception as e:
                continue

            with self.pagestack(self.driver):
                WorkerStats().add("page_view", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))
                num = random.randint(0, 9)
                if (not num) or Setting.DEBUG:
                    try:
                        oldtile = self.driver.title
                        self.driver.execute_script(
                            "document.getElementById('newsDetailAd').getElementsByTagName('img')[0].click();")
                        time.sleep(3)  # 必须等待异步完成
                        newtile = self.driver.title
                        if oldtile == newtile:  # 点击没有跳转
                            logger.warning("Worker[%s] ad click do not jump" % (self.name,))
                            self.proxystatus = False
                            return

                        self.driver.execute_script("var q=document.documentElement.scrollTop=10000")
                        WorkerStats().add("ad_click", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))

                    except Exception as e:
                        if hasattr(e, "msg") and "null is not an object" in e.msg:
                            WorkerStats().add("ad_not_load", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))

        WorkerStats().add("action3", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))

    @contextmanager
    def pagestack(self, driver):
        driver.switch_to_window(driver.window_handles[-1])
        try:
            yield
        except Exception as e:
            self.proxystatus = False
            logger.error("Worker[%s] error %s" % (self.name, e))
        else:
            driver.close()
            driver.switch_to_window(driver.window_handles[-1])

    def action_99(self, *args, **kwargs):
        self.driver.close()
        self.driver.quit()
        WorkerStats().add("action99", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))


def handle_worker(name):
    while True:
        time.sleep(1)
        try:
            if not Setting.DEBUG:
                precent = time_control(datetime.now().hour, datetime.now().minute)
                total = random.Random().randint(0, 100)
                if precent > total:
                    time.sleep(30)

            if ProxyPool().length < 1:
                continue
            else:
                try:
                    new_proxy = ProxyPool().lpop()
                    logger.info("Worker[%s] get a new proxy [%s:%s] ProxyPool length=%s" % (name, new_proxy["ip"], new_proxy["port"], ProxyPool().length))
                except Exception as e:
                    continue

                if new_proxy["times"] > Setting.IP_USE_TIMES:
                    continue

                if new_proxy["timestamp"] and int(time.time()) - new_proxy["timestamp"] < 60 * 10:
                    time.sleep(30)
                    ProxyPool().rpush(new_proxy)
                    continue

                ip = new_proxy["ip"]
                port = new_proxy["port"]

                if clean_proxyip(ip, port):
                    logger.info("Worker[%s] get a normal proxy [%s:%s] IP_POOL length=%s" % (name, ip, port, ProxyPool().length))
                    worker = Worker(name, ip, port)
                    [worker.do(a) for a in worker.list()]
                    logger.info("Worker[%s] finish action with proxy [%s:%s]" % (name, ip, port))

                    if worker.proxystatus:
                        WorkerStats().add("all_success", 1, kp=(datetime.now().strftime("%Y-%m-%d"),))
                        new_proxy["times"] += 1
                        new_proxy["timestamp"] = int(time.time())
                        ProxyPool().rpush(new_proxy)
        except Exception as e:
            logger.info("Worker[%s] error %e" % (name, e))


def time_control(hour, minute):
    if hour < 9:
        precent = Setting.WORKER_NUMS / (9 * 60) * (hour * 60 + minute)

    elif hour > 18:
        precent = Setting.WORKER_NUMS / (6 * 60) * ((24 - hour) * 60 - minute)

    else:
        precent = (5 + 5 * sin((hour * 60 + minute + 70)/30)) * Setting.WORKER_NUMS / 10

    return int(precent) + 1


def debug_run(ip_port):
    ip, port = ip_port.split(":")
    worker = Worker("DEBUG", ip, port)
    logger.debug("worker module created instance worker[%s:%s]" % (ip, port))

    worker.action_1()
    logger.debug('worker module finish action1')

    worker.action_2()
    logger.debug('worker module finish action2')

    worker.action_3()
    logger.debug('worker module finish action3')

    worker.action_99()
    logger.debug('worker module finish action99')


def work_run():
        WORKER_POOL = threadpool.ThreadPool(Setting.WORKER_NUMS)
        logger.debug("worker module created WORKER_POOL[%s] " % Setting.WORKER_NUMS)

        requests = threadpool.makeRequests(handle_worker, [i for i in range(Setting.WORKER_NUMS)])
        [WORKER_POOL.putRequest(req) for req in requests]
        WORKER_POOL.wait()


def work_start():
    logger.info("worker module starting...")
    if Setting.DEBUG:
        debug_run("192.168.10.222:8888")
    else:
        try:
            work_run()
        except Exception as e:
            logger.error("worker module error %s" % e)
